/*异步工作器
*
*   停止工作 
*   添加数据
*
*/

#ifndef _LOOPER_H_
#define _LOOPER_H_

#include "buffer.hpp"
#include <mutex>
#include <condition_variable>
#include <thread>
#include <functional>
#include <memory>
#include <atomic>

namespace Log
{
    //异步工作线程回调函数类型
    using Functor = std::function<void(Buffer&)>;
    
    enum class Async_Type
    {
        ASYNC_SAFE,  //安全的，异步工作器如果buffer满了就阻塞
        ASYNC_UNSAFE //不安全的，异步工作器如果buffer满了就扩容，由于性能测试
    };

    //异步工作器
    class AsyncLooper
    {
    public:
        using ptr = std::shared_ptr<AsyncLooper>;

        AsyncLooper(const Functor& cb,Async_Type looper_type = Async_Type::ASYNC_SAFE)
        :_callBack(cb)
        ,_looper_type(looper_type)
        ,_stop(false)
        ,_thread(&AsyncLooper::threadFunc,this) {}

        //析构日志工作器 必须等消费者线程消费完数据才能退出
        ~AsyncLooper() { stop(); } 

        //停止工作
        void stop()
        {
            //标记为停止
            _stop = true;
            //唤醒消费者赶紧消费日志
            _con_cond.notify_all();
            //等待工作线程退出
            _thread.join();
        }

        //写入数据
        void push(const char* data,size_t len)
        {
            //如果日志器停止 则停止写入数据
            if(_stop) return;

            //上锁
            std::unique_lock<std::mutex> _lock(_mutex);

            //如果是安全模式 则阻塞运行 否则buffer扩容
            if(_looper_type == Async_Type::ASYNC_SAFE)
                //判断是否可以写入数据 如果不行则挂起 等待消费者交换缓冲区
                _pro_cond.wait(_lock,[&]() { return _pro_buf.writeAbleSize() >= len; });

            //写入数据
            _pro_buf.push(data,len);
            //唤醒消费线程
            _con_cond.notify_all();
        }

    private:
        //线程入口函数
        void threadFunc()
        {
            //如果停止标志位为 false 则继续工作
            while(true)
            {
                //判断是否需要更换缓冲区
                {
                    std::unique_lock<std::mutex> _lock(_mutex);

                    //判断生产者队列是否有数据 或 退出前被唤醒(如果准备停止则需要消费完生产者最后的数据) -> 没有则挂起
                    _con_cond.wait(_lock,[&]() { return _stop || !_pro_buf.empty(); });

                    //如果退出标志位被标记 且 生产者缓冲区没有数据 再退出 否则退出时生产者缓冲区中仍然有数据
                    if(_stop && _pro_buf.empty()) break;

                    //此时有数据 准备交换
                    _con_buf.swap(_pro_buf);

                    //如果是安全模式 才唤醒阻塞的生产者
                    if(_looper_type == Async_Type::ASYNC_SAFE)
                        //唤醒生产者
                        _pro_cond.notify_all();
                }
                //如果此时消费者buf为空则不执行任何业务函数
                //if(_con_buf.empty()) continue;

                //调用回调函数落地日志
                _callBack(_con_buf);
                //重置日志队列
                _con_buf.reset();
            }

        }
    private:
        Functor _callBack; //业务回调函数 对缓冲区的数据进行处理 该函数由异步工作器使用者传入
    private:
        Async_Type _looper_type; //异步工作器是否安全 默认安全模式
        std::atomic<bool> _stop; //异步工作器停止标志 true停止 原子操作保证线程安全
        Buffer _pro_buf; //生产者缓冲区
        Buffer _con_buf; //消费者缓冲区
        std::mutex _mutex; //互斥锁
        std::condition_variable _pro_cond; //生产者条件变量
        std::condition_variable _con_cond; //消费者条件变量
        std::thread _thread; //异步工作线程(落实日志写入)
    };
}

#endif